package util;

import csvreader.CsvReader;
import org.apache.log4j.Logger;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.StringReader;
import java.util.*;

/**
 * User: pradeep
 * Date: Mar 22, 2010
 * Time: 12:16:56 PM
 * Utility class for "Chemical Similarity Map" Application (actual logic implemented here)
 */
public class ChemSimilarityUtil
{
    Logger gLogger = Logger.getLogger(ChemSimilarityUtil.class);
    private static ChemSimilarityUtil chemUtil = null;

    double num[][] = null;
    int rowCount = 0;
    int colCount = 0;
    double probabilityFactor = 0.7;

    /**
     * get instance of util class
     * @return
     */
    public static ChemSimilarityUtil getInstance(){
        if( chemUtil == null ){
            chemUtil = new ChemSimilarityUtil();
        }
        return chemUtil;
    }

    /**
     * process method to calculate chemical similarity 
     * @param data
     * @param probFactor
     * @param networkApprochType
     * @param appsType 
     * @return
     */
    public String processChemicalSimilarity(String data,double probFactor,String networkApprochType, String appsType)
    {
        String chemicalSimilarityResult = "";
        try
        {
            if(probFactor > 0 && probFactor <= 1.0){
                this.probabilityFactor =  probFactor;
            }
            gLogger.info ("prepare 2D matrix outoff data");
            // prepare 2D matrix outoff data 
            double numMatrix[][] = createMatrix(data);

            // Call process implementation metahod
            chemicalSimilarityResult = processData(numMatrix,this.probabilityFactor,networkApprochType,appsType);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return chemicalSimilarityResult;
    }

    /**
     * to get the delimiter char for csv data
     * @param string
     * @return
     */
    public char getDelimiter(String string)
    {
        char delimiter='-';
        if( string.contains(",") )
        {
            delimiter = ',';
        }else
        if( string.contains("\t") )
        {
            delimiter = '\t';
        }
        return delimiter;
    }

    /**
     * read csv file and create 2-D matrix (one column extra for manuplations)
     * @param data
     * @return double[][]
     */
    public double[][] createMatrix(String data){
        double numMatrix[][] = null;
        try
        {
            char delimiter = getDelimiter(data);
            StringReader reader1 = new StringReader(data);
            CsvReader reader = new CsvReader(reader1,delimiter);
            reader.readRecord();
            rowCount = reader.getColumnCount();
            colCount = rowCount;
            gLogger.info("rowCount :: "+rowCount);
            gLogger.info("colCount :: "+colCount);
            numMatrix = new double[rowCount][colCount+1];  // one extra column for manuplations

            int rowId = 0 ;

            do{
                for(int colId=0; colId<colCount ; colId++ )
                {
                    if( rowId ==0 && colId ==0 ){
                        numMatrix[rowId][colId] = 0; // make upper-left column blank by putting Zero
                    }else{
                        numMatrix[rowId][colId] = Double.parseDouble("0"+reader.get(colId));
                    }
                }
                rowId++;
            }while(reader.readRecord());
            gLogger.info("Matrix creation done");
            reader.close();
        }catch (Exception e){
            e.printStackTrace();
        }
        return numMatrix;
    }

    /**
     * main logic to calculate similarity map  
     * @param numMat
     * @param probFactor
     * @param networkApprochType
     * @param appsType
     * @return
     */
    public String processData(double[][] numMat,double probFactor,String networkApprochType, String appsType){
       String chemicalSimilarityResult = "";
       try{
           num = numMat;

           if(probFactor > 0 && probFactor <= 1.0){
                this.probabilityFactor =  probFactor;
           }


           showTable();
           gLogger.info("Step-1: Upper/Lower Triangular Matrix");
           // Step-1: Create Upper/Lower Triangular Matrix
           if(networkApprochType.equalsIgnoreCase("UpperTriangularNetwork")){ // Process on the bases of Upper Triangular Matrix

                getUpperTriangularMatrix();

           }else{ // Process on the bases of Lower Triangular Matrix
               
                getLowerTriangularMatrix();
           }
           showTable();

           gLogger.info("Step-2: Create Max Valued Matrix");
           // Step-2: Create Max Valued Matrix
           getMaxValueMatrix();
           showTable();

           gLogger.info("Step-3: Create Probability Matrix");                          
           // Step-3: Create Probability Matrix
           getProbabilityMatrix(networkApprochType);
           showTable();
           
           if(appsType.equalsIgnoreCase("EdgeAttribute")){
                gLogger.info("Step-4: Calculate Edge Attribute"); 
                // Calculate Edge Attribute
                chemicalSimilarityResult = getEdgeAttributeResult(networkApprochType);
           }else if(appsType.equalsIgnoreCase("SimilarityMap")){
               gLogger.info("Step-4: Calculate Similarity Map");
               // Step-4: Calculate Similarity Map
               chemicalSimilarityResult = getSimilarityMapResult(networkApprochType);
           }           
           gLogger.info("Done");
           
       }catch(Exception e){
           e.printStackTrace();
       }
       return chemicalSimilarityResult;
    }




    /**
     * get Edge Attribute Result
     * @return
     */
    private String getEdgeAttributeResult(String networkApprochType)
    {
        String edgeAttributeResult = "";
        //String str="";
        Set lArrList = new HashSet();
        FileOutputStream fileStreamOutput = null;
        File tempFile = null;
        try{
            //tempFile = File.createTempFile("MyFile.txt", ".tmp" );
            String fileName = FileUtility.getInstance().getRendomFileName();
            tempFile = new File(fileName+".txt");
            fileStreamOutput = new FileOutputStream(tempFile);

            if(networkApprochType.equalsIgnoreCase("UpperTriangularNetwork")){
                for(int rowId=1; rowId<rowCount ; rowId++ )
                {
                    //gLogger.info("rowId :: "+rowId);
                    for(int colId=rowId+1; colId<colCount ; colId++ )
                    {
                        if( num[rowId][colId] != 0.0 )
                        {
                            edgeAttributeResult = Math.round( num[rowId][0] ) + " (tm) "+ Math.round( num[0][colId] )+ "\t" + num[rowId][colId];
                            if(!lArrList.contains(edgeAttributeResult)){
                               if(lArrList.size()==0){
                                   fileStreamOutput.write(edgeAttributeResult.getBytes());
                               }else{
                                   fileStreamOutput.write( ("\n"+edgeAttributeResult).getBytes());
                               }
                               lArrList.add(edgeAttributeResult);
                            }
                        }
                    }
                }
            }else{
                for(int rowId=1; rowId<rowCount ; rowId++ )
                {
                    //gLogger.info("rowId :: "+rowId);
                    for(int colId=1; colId<rowId ; colId++ )
                    {
                        if( num[rowId][colId] != 0.0 )
                        {
                            edgeAttributeResult = Math.round( num[rowId][0] ) + " (tm) "+ Math.round( num[0][colId] )+ "\t" + num[rowId][colId];
                            if(!lArrList.contains(edgeAttributeResult)){
                               if(lArrList.size()==0){
                                   fileStreamOutput.write(edgeAttributeResult.getBytes());
                               }else{
                                   fileStreamOutput.write( ("\n"+edgeAttributeResult).getBytes());
                               }
                               lArrList.add(edgeAttributeResult);
                            }
                        }
                    }
                }
            }

            if(tempFile.exists()){
                edgeAttributeResult =  new String(FileUtility.getInstance().getBytesFromFile(tempFile));
            }else{
                edgeAttributeResult = "";
            }

            lArrList = null;

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                fileStreamOutput.flush();
                fileStreamOutput.close();
                if(tempFile!=null && tempFile.exists()){
                    tempFile.delete();                    
                }
            }catch(Exception e){
                
            }
        }


        return edgeAttributeResult;
    }

    /**
     * get SimilarityMap Result
     * @return
     */
    private String getSimilarityMapResult(String networkApprochType)
    {
        String similarityMapResult = "";
        Set lArrList = new HashSet();
        FileOutputStream fileStreamOutput = null;
        File tempFile = null;
        try{
            String fileName = FileUtility.getInstance().getRendomFileName();
            tempFile = new File(fileName+".txt");
            fileStreamOutput = new FileOutputStream(tempFile);


            for(int rowId=2; rowId<rowCount ; rowId++ )
            {
                similarityMapResult = Math.round( num[rowId][0] )+ "\ttm\t"+ Math.round( num[rowId][colCount] );
                if(!lArrList.contains(similarityMapResult)){
                   if(lArrList.size()==0){
                       fileStreamOutput.write(similarityMapResult.getBytes());
                   }else{
                       fileStreamOutput.write( ("\n"+similarityMapResult).getBytes());
                   }
                   lArrList.add(similarityMapResult);
                }
            }
            if(networkApprochType.equalsIgnoreCase("UpperTriangularNetwork")){
                for(int rowId=1; rowId<rowCount ; rowId++ )
                {
                    //gLogger.info("rowId :: "+rowId);
                    for(int colId=rowId+1; colId<colCount ; colId++ )
                    {
                        if( num[rowId][colId] != 0.0 )
                        {
                            similarityMapResult = Math.round( num[rowId][0] ) + "\ttm\t"+ Math.round( num[0][colId] );

                            if(!lArrList.contains(similarityMapResult)){
                               if(lArrList.size()==0){
                                   fileStreamOutput.write(similarityMapResult.getBytes());
                               }else{
                                   fileStreamOutput.write( ("\n"+similarityMapResult).getBytes());
                               }
                               lArrList.add(similarityMapResult);
                            }
                        }
                    }
                }
            }else{
                for(int rowId=1; rowId<rowCount ; rowId++ )
                {
                    //gLogger.info("rowId :: "+rowId);
                    for(int colId=1; colId<rowId ; colId++ )
                    {
                        if( num[rowId][colId] != 0.0 )
                        {
                            similarityMapResult = Math.round( num[rowId][0] ) + "\ttm\t"+ Math.round( num[0][colId] );
                            if(!lArrList.contains(similarityMapResult)){
                               if(lArrList.size()==0){
                                   fileStreamOutput.write(similarityMapResult.getBytes());
                               }else{
                                   fileStreamOutput.write( ("\n"+similarityMapResult).getBytes());
                               }
                               lArrList.add(similarityMapResult);
                            }
                        }
                    }
                }
            }

            if(tempFile.exists()){
                similarityMapResult =  new String(FileUtility.getInstance().getBytesFromFile(tempFile));
            }else{
                similarityMapResult = "";
            }

            lArrList = null;
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                fileStreamOutput.flush();
                fileStreamOutput.close();
                if(tempFile!=null && tempFile.exists()){
                    tempFile.delete();
                }
            }catch(Exception e){

            }
        }    

        return similarityMapResult;
    }

    /**
     * get Probability Upper Triangular Matrix
     */
    private void getProbabilityMatrix(String type)
    {
        gLogger.info("probabilityFactor :: "+probabilityFactor);
        if(type.equalsIgnoreCase("UpperTriangularNetwork")){
            for(int rowId=1; rowId<rowCount ; rowId++ )
            {
                for(int colId=rowId+1; colId<colCount ; colId++ )
                {
                    // remove all cells who has lesser value than probabilityFactor
                    if( num[rowId][colId] < probabilityFactor )
                    {
                        num[rowId][colId]=0;
                    }
                }
            }
        }else{
            for(int rowId=1; rowId<rowCount ; rowId++ )
            {
                for(int colId=1; colId<rowId ; colId++ )
                {
                    // remove all cells who has lesser value than probabilityFactor
                    if( num[rowId][colId] < probabilityFactor )
                    {
                        num[rowId][colId]=0;
                    }
                }
            }
        }               
    }

    /**
     * to create max value matrix
     * Description : Add one more column at the last of each row which contains max valued cell's first-column value
     */
    private void getMaxValueMatrix()
    {
        for(int rowId=1; rowId<rowCount ; rowId++ )
        {
            int id = getMaximumListValueIndex(num[rowId]);
            if(id!=-1)
            {
                num[rowId][colCount] = num[0][id];
            }
        }
    }

    /**
     * get Maximum List Value Index
     * @param val
     * @return
     */
    private int getMaximumListValueIndex(double[] val)
    {
        double res=-1;
        int resIndex=-1;
        try
        {
            for(int index=1; index<val.length-1 ; index++ )
            {
                if( res < val[index]  ){
                    resIndex = index;
                    res = val[index];
                }

            }
        }catch(Exception e)
        {
            e.printStackTrace();
        }

        return resIndex;
    }

    /**
     * transform into lower triangular matrix
     */
    private void getLowerTriangularMatrix()
    {
        for(int rowId=1; rowId<rowCount ; rowId++ )
        {
            for(int colId=1; colId<colCount ; colId++ )
            {
                if( rowId <= colId)
                    num[rowId][colId] = num[rowId][colId] - num[colId][rowId];
            }
        }
    }

    /**
     * transform into upper triangular matrix
     */
    private void getUpperTriangularMatrix()
    {
        for(int rowId=1; rowId<rowCount ; rowId++ )
        {
            for(int colId=1; colId<colCount ; colId++ )
            {
                if( rowId >= colId)
                    num[rowId][colId] = num[rowId][colId] - num[colId][rowId];
            }
        }

    }

    /**
     * transform list into string stream
     * @param dataList
     * @return
     */
    public String getStringStream(List<String> dataList){
        String resultStream = "";
        for (String string : dataList) {
          if(resultStream.trim().length()==0){
              resultStream = string;
          }else{
              resultStream += "\n" + string;
          }
        }
        return resultStream;
    }

    /**
     * to print the matrix
     */
    private void showTable()
    {
//        for(int rowId=0; rowId<rowCount ; rowId++ )
//        {
//            for(int colId=0; colId<colCount+1 ; colId++ )
//            {
//                System.out.print( num[rowId][colId] );
//                System.out.print("\t");
//            }
//            System.out.println("");
//        }
    }

}
